package cn.anoxia.springframework.context.event;

import cn.anoxia.springframework.beans.BeanFactory;
import cn.anoxia.springframework.beans.BeansException;
import cn.anoxia.springframework.beans.factory.BeanFactoryAware;
import cn.anoxia.springframework.context.ApplicationEvent;
import cn.anoxia.springframework.context.ApplicationListener;
import cn.anoxia.springframework.utils.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

/**
 * The class AbstractApplicationEventMulticaster.
 * <p>
 * Description:
 *
 * @author: huangle
 * @since: 2021/08/30 10:25
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

  public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

  private BeanFactory beanFactory;

  @Override
  public void addApplicationListener(ApplicationListener<?> listener) {
    applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
  }

  @Override
  public void removeApplicationListener(ApplicationListener<?> listener) {
    applicationListeners.remove(listener);
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) {
    this.beanFactory = beanFactory;
  }

  protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) throws BeansException {
    LinkedList<ApplicationListener> allListeners = new LinkedList<>();
    for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
      if (supportsEvent(listener,event)){
        allListeners.add(listener);
      }
    }
    return allListeners;
  }

  protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) throws BeansException {
    Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
    // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型，需要判断后获取目标 class
    Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
    Type genericInterface = targetClass.getGenericInterfaces()[0];

    Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];

    String typeName = actualTypeArgument.getTypeName();
    Class<?> eventClassName;
    try {
      eventClassName = Class.forName(typeName);
    }catch (ClassNotFoundException e){
      throw new BeansException("事件名称错误："+typeName);
    }
    // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同，或是否是其超类或超接口。
    // isAssignableFrom是用来判断子类和父类的关系的，或者接口的实现类和接口的关系的，默认所有的类的终极父类都是Object。
    // 如果A.isAssignableFrom(B)结果是true，证明B可以转换成为A,也就是A可以由B转换而来。
    return eventClassName.isAssignableFrom(event.getClass());
  }
}
